// 
// ButtonBase.cs
//  
// Author:
//       bostjan <>
// 
// Copyright (c) 2009 bostjan
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using Cairo;

namespace Kodre.GtkWidgets
{
	
	
	[System.ComponentModel.ToolboxItem(true)]
	public class ButtonBase : Gtk.DrawingArea
	{
		
		#region - Properties -
		
		#region - Private Variables -
		
		protected bool mouseDown = false;
		protected bool mouseOver = false;
		protected Pango.Layout layout;
	
		#endregion
		
		#region -  Text  -
		
		public String Text
		{
			get{return layout.Text;}
		 	set
			{
				Rectangle rcBefore=GetTextArea();
				layout.SetText(value);
				Rectangle rcAfter=GetTextArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		protected Gdk.Color foreColor = 
			CairoUtils.ColorGdkFromDrawingColor(System.Drawing.Color.Black);
		public System.Drawing.Color ForeColor
			{
			get{return CairoUtils.ColorDrawingFromGdkColor(foreColor);}
			set
			{
				Rectangle rc=GetTextArea();
				foreColor=CairoUtils.ColorGdkFromDrawingColor(value);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}			
		}
		
		public int FontSize
		{
			get{return layout.FontDescription.Size/(int)Pango.Scale.PangoScale;}
			set
			{
				Rectangle rcBefore=GetTextArea();
				layout.FontDescription.Size = value*(int)Pango.Scale.PangoScale;
				Rectangle rcAfter=GetTextArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		public Pango.Weight FontWeight
		{
			get{return layout.FontDescription.Weight;}
			set
			{
				Rectangle rcBefore=GetTextArea();
				layout.FontDescription.Weight= value;
				Rectangle rcAfter=GetTextArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		public Pango.Style FontStyle
		{
			get{return layout.FontDescription.Style;}
			set
			{
				Rectangle rcBefore=GetTextArea();
				layout.FontDescription.Style= value;
				Rectangle rcAfter=GetTextArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		public String Font
		{
			get{return layout.FontDescription.Family;}
		 	set
			{	
				Rectangle rcBefore=GetTextArea();
				layout.FontDescription.Family = value;
				Rectangle rcAfter=GetTextArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		protected VerticalAllign textVerticalAlign= VerticalAllign.Middle;
		public Allign TextAllign
		{
			get
			{
				HorizontalAllign ha;
				if(layout.Alignment== Pango.Alignment.Left)
					ha = HorizontalAllign.Left;
				else if(layout.Alignment== Pango.Alignment.Center)
					ha= HorizontalAllign.Center;
				else
					ha=HorizontalAllign.Right;
				
				return 	(Allign)((int)ha | (int)textVerticalAlign);
			}
			set
			{
				Rectangle rcBefore=GetTextArea();
				if((VerticalAllign)((int)value & (int)VerticalAllign.Bottom) 
				   == VerticalAllign.Bottom)
					textVerticalAlign=VerticalAllign.Bottom;
				else if((VerticalAllign)((int)value & (int)VerticalAllign.Middle)
				        == VerticalAllign.Middle)
					textVerticalAlign=VerticalAllign.Middle;
				else 
					textVerticalAlign=VerticalAllign.Top;
				
				if((HorizontalAllign)((int)value & (int)HorizontalAllign.Left) 
				   == HorizontalAllign.Left)
					layout.Alignment=Pango.Alignment.Left;
				else if((HorizontalAllign)((int)value & (int)HorizontalAllign.Center)
				        == HorizontalAllign.Center)
					layout.Alignment=Pango.Alignment.Center;
				else 
					layout.Alignment=Pango.Alignment.Right;
				
				Rectangle rcAfter=GetTextArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
				
			}
		}
		
		#endregion
		
		#region -  Image  -
		protected Gdk.Pixbuf image;
		public Gdk.Pixbuf Image 
		{
			get 
			{
				return image;
			}
			set 
			{
				Rectangle rcBefore=GetImageArea();
				image = value;
				Rectangle rcAfter=GetImageArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		protected Allign imageAllign = Allign.MiddleLeft;
		public Allign ImageAllign
		{
			get{return imageAllign;}
			set
			{
				Rectangle rcBefore=GetImageArea();
				imageAllign=value;
				Rectangle rcAfter=GetImageArea();
				Rectangle rc=CairoUtils.RectangleUnion(rcBefore,rcAfter);
				if(!CairoUtils.IsRectangleNull(rc))
					QueueDrawArea((int)rc.X,(int)rc.Y,(int)rc.Width,(int)rc.Height);
			}
		}
		
		#endregion
		
		#endregion
		
		#region - Events -
		
		public delegate void PressedEventHandler(object sender, EventArgs e);
		private PressedEventHandler PressedEvent;
		
		public event PressedEventHandler Pressed
		{
			add{
				PressedEvent =(PressedEventHandler)System.Delegate.Combine(PressedEvent, value);
			}
			remove{
				PressedEvent =(PressedEventHandler)System.Delegate.Remove(PressedEvent, value);
			}
		}
		
		public delegate void ReleasedEventHandler(object sender, EventArgs e);
		private ReleasedEventHandler ReleasedEvent;
		
		public event ReleasedEventHandler Released
		{
			add{
				ReleasedEvent =(ReleasedEventHandler)System.Delegate.Combine(ReleasedEvent, value);
			}
			remove{
				ReleasedEvent =(ReleasedEventHandler)System.Delegate.Remove(ReleasedEvent, value);
			}
		}
		
		#endregion
		
		public ButtonBase()
		{
			this.Events = Gdk.EventMask.ButtonPressMask |
				Gdk.EventMask.ButtonReleaseMask |
				Gdk.EventMask.KeyPressMask |
				Gdk.EventMask.KeyReleaseMask |
				Gdk.EventMask.EnterNotifyMask |
				Gdk.EventMask.LeaveNotifyMask |
				Gdk.EventMask.FocusChangeMask;
			
			
			CanFocus=true;
			
			
			layout = new Pango.Layout(this.CreatePangoContext());
			layout.Wrap = Pango.WrapMode.Word;
			layout.FontDescription= new Pango.FontDescription();
			layout.Alignment=Pango.Alignment.Center;
			layout.FontDescription.Family="Arial";
			layout.FontDescription.Size = 10*(int)Pango.Scale.PangoScale;
		}
		
		protected override bool OnButtonPressEvent (Gdk.EventButton ev)
		{
			this.GrabFocus();
			if(ev.Button==1)
			{
				mouseDown=true;
				QueueDraw();	
				if(PressedEvent!=null)
					PressedEvent(this,new EventArgs());
			}
			return base.OnButtonPressEvent (ev);
		}
		
		protected override bool OnButtonReleaseEvent (Gdk.EventButton ev)
		{
			if(ev.Button==1)
			{
				mouseDown=false;
				QueueDraw();	
				if(ReleasedEvent!=null)
					ReleasedEvent(this,new EventArgs());
			}
			return base.OnButtonReleaseEvent (ev);
		}
		
		protected override bool OnKeyPressEvent (Gdk.EventKey evnt)
		{
			if(evnt.Key == Gdk.Key.space || evnt.Key == Gdk.Key.Return || evnt.Key==Gdk.Key.KP_Enter)
			{
				mouseDown=true;
				QueueDraw();	
				if(PressedEvent!=null)
					PressedEvent(this,new EventArgs());
			}
			return base.OnKeyPressEvent (evnt);
		}

		protected override bool OnKeyReleaseEvent (Gdk.EventKey evnt)
		{
			if(evnt.Key == Gdk.Key.space || evnt.Key == Gdk.Key.Return || evnt.Key==Gdk.Key.KP_Enter )
			{
				mouseDown=false;
				QueueDraw();	
				if(ReleasedEvent!=null)
					ReleasedEvent(this,new EventArgs());
			}
			return base.OnKeyReleaseEvent (evnt);
		}
		
		protected override bool OnEnterNotifyEvent (Gdk.EventCrossing evnt)
		{
 			mouseOver=true;
			QueueDraw();
			return base.OnEnterNotifyEvent (evnt);
		}

		protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing evnt)
		{
			mouseOver=false;
			QueueDraw();
			return base.OnLeaveNotifyEvent (evnt);
		}
		
		protected override bool OnExposeEvent(Gdk.EventExpose ev)
		{
			
			Cairo.Context cr =  Gdk.CairoHelper.Create(ev.Window);
			
			DrawBackground(cr, ev);
			DrawBorder(cr, ev);
			DrawImage(cr, ev);
			DrawText(cr, ev);
			
			((IDisposable) cr.Target).Dispose ();
        	((IDisposable) cr).Dispose ();
			
			return true;
		}
		
		protected virtual void DrawBackground(Cairo.Context cr, Gdk.EventExpose ev)
		{
		}
		
		protected virtual void DrawBorder(Cairo.Context cr, Gdk.EventExpose ev)
		{
		}
		
		protected virtual void DrawImage(Cairo.Context cr, Gdk.EventExpose ev)
		{
		}
		
		protected virtual void DrawText(Cairo.Context cr, Gdk.EventExpose ev)
		{
		}
		
		// <summary>
		/// Calculates text position, which depends on TextAllign
		/// </summary>
		/// <returns>
		/// Uper left point of text.
		/// A <see cref="Point"/>
		/// </returns>
		protected virtual Point GetTextPosition()
		{
			int h, w;
			layout.GetPixelSize(out w, out h);
			if(textVerticalAlign  == VerticalAllign.Bottom)
				h = this.Allocation.Height - h;
			else if(textVerticalAlign == VerticalAllign.Middle)
				h = this.Allocation.Height/2 - h/2;
			else 
				h = 0;
			return new Point(0,h);
		}
		
		/// <summary>
		/// Calculates area where text is drawn
		/// </summary>
		/// <returns>
		/// A <see cref="Rectangle"/>
		/// </returns>
		protected virtual Rectangle GetTextArea()
		{
			int h, w;
			int y;
			Pango.Rectangle inkRect;
			Pango.Rectangle logRect;
			layout.GetPixelExtents(out inkRect,out logRect);
			layout.GetPixelSize(out w, out h);
			if(textVerticalAlign  == VerticalAllign.Bottom)
				y = this.Allocation.Height - h;
			else if(textVerticalAlign == VerticalAllign.Middle)
				y = this.Allocation.Height/2 - h/2;
			else 
				y = 0;
			return new Rectangle(logRect.X,y,w,h);
		}
		
		protected virtual Point GetImagePosition()
		{
			int x=0;
			int y=0;			
			int imageOffset=3;
			
			if(image!=null)
			{
				if((VerticalAllign)((int)imageAllign & (int)VerticalAllign.Bottom) == VerticalAllign.Bottom)
					y = this.Allocation.Height - imageOffset - image.Height;
				else if((VerticalAllign)((int)imageAllign & (int)VerticalAllign.Middle) == VerticalAllign.Middle)
					y = this.Allocation.Height/2 - this.image.Height/2;
				else 
					y = imageOffset;
						
				if((HorizontalAllign)((int)imageAllign & (int)HorizontalAllign.Center) == HorizontalAllign.Center)
					x = this.Allocation.Width / 2 - this.image.Width /2;
				else if((HorizontalAllign)((int)imageAllign & (int)HorizontalAllign.Right) == HorizontalAllign.Right)
				{
					x = this.Allocation.Width - imageOffset - this.image.Width;
				}
				else 
				{
					x =imageOffset;
				}
			}
			
			return new Point(x,y);
		}
		
		protected virtual Rectangle GetImageArea()
		{
			Rectangle rect;
			if(image!=null)
			{
				Point pt=GetImagePosition();
				rect = new Rectangle(pt.X,pt.Y,image.Width,image.Height);
			}
			else
				rect = new Rectangle (0,0,0,0);
			
			return rect;		
		}
		
		protected override void OnSizeAllocated(Gdk.Rectangle allocation)
		{
			base.OnSizeAllocated(allocation);
			// Insert layout code here.
		}
		
		protected override void OnSizeRequested(ref Gtk.Requisition requisition)
		{
			// Calculate desired size here.
			requisition.Height = 50;
			requisition.Width = 50;
		}
	}
}
